#include "sound.h"
#include "fft.h"
sound::sound()
{}

sound::sound(const std::string& filename)
{
    load_file(filename);
}

void sound::load_file(const std::string& filename)
{
    ifstream in(filename, std::ios::binary);
    if (not in)
        throw file_not_found_exception(filename);
    in.read(reinterpret_cast<char*>(this), offsetof(sound, additional_information));
    if (bits not_eq 8 and bits not_eq 16)
        throw parse_exception();
    if (pcm_bytes > 16)
    {
        additional_information = new char[pcm_bytes - 16];
        in.read(additional_information, pcm_bytes - 16);
    }
    char fact_data[5];
    in.read(fact_data, 4);
    fact_data[4] = '\0';
    if (std::strcmp(fact_data, "fact") == 0)
    {
        has_fact = true;
        in.read(reinterpret_cast<char*>(std::addressof(fact_bytes)), 4);
        in.read(fact, fact_bytes);
        in.read(fact_data, 4);
        if (std::strcmp(fact_data, "data"))
            throw parse_exception();
    }
    else if (std::strcmp(fact_data, "data") == 0)
        has_fact = false;
    else
        throw parse_exception();
    in.read(reinterpret_cast<char*>(std::addressof(data_bytes)), 4);
    data._8 = new int8_t[data_bytes];
    in.read(reinterpret_cast<char*>(data._8), data_bytes);

    data_count = data_bytes >> (bits >> 4);
}

std::ostream& operator<<(std::ostream& o, const sound& s)
{
    for (int i = 0; i < 4; ++i)
        o << s.riff[i];
    o << "\nbytes of data: " << s.size << '\n';
    for (int i = 0; i < 8; ++i)
        o << s.wavefmt[i];
    o << "\nbytes of pcm wave format: " << s.pcm_bytes
      << "\nchannel(s): " << s.channel
      << "\nfrequency: " << s.frequency
      << "\nbytes per second: " << s.bytes_per_second
      << "\nbytes in one block: " << s.block
      << "\nbits in one channel: " << s.bits;
    if (s.additional_information)
        o << "\nadditional information: " << s.additional_information;
    o << "\nfact: ";
    if (s.has_fact)
        o << s.fact;
    else
        o << "no such field";
    return o << "\nbytes of data: " << s.data_bytes << '\n';
}

double sound::average()
{
    int64_t sum = 0;
    switch (bits)
    {
    case 8:
        for (int i = 0; i < data_count; i++)
            sum += data._8[i];
        break;
    case 16:
        for (int i = 0; i < data_count; i++)
            sum += data._16[i];
        break;
    }
    return static_cast<double>(sum) / data_count / 0x7FFF;
}

double sound::mean_squared(double average_value, double var)
{
    if (not std::isnan(average_value) and not std::isnan(var))
        return var + average_value * average_value;
    int64_t sum = 0;
    switch (bits)
    {
    case 8:
        for (int i = 0; i < data_count; i++)
            sum += data._8[i] * data._8[i];
        break;
    case 16:
        for (int i = 0; i < data_count; i++)
            sum += data._16[i] * data._16[i];
        break;
    }
    return static_cast<double>(sum) / data_count / (static_cast<int32_t>(0x7FFF) * 0x7FFF);
}

double sound::variance(double average_value, double ms)
{
    average_value = std::isnan(average_value) ? average() : average_value;
    ms = std::isnan(ms) ? mean_squared() : ms;
    return ms - average_value * average_value;
}

double sound::seconds()
{
    return static_cast<double>(data_bytes) / bytes_per_second;
}

double sound::normalized(int i)
{
    switch (bits)
    {
    case 8:
        return data._8[i] / static_cast<double>(0x7F);
    case 16:
        return data._16[i] / static_cast<double>(0x7FFF);
    }
}

std::string sound::statistic_description()
{
    std::stringstream o;
    o << (*this)
      << "\naverage: " << average()
      << "\nmean squared: " << mean_squared()
      << "\nvariance: " << variance();
    return o.str();
}

//std::array<double, 8192> sound::power_spectrum_real_part()
//{
//    std::vector<Complex> x;
//    switch (bits)
//    {
//    case 8:
//        fft::compute(data._8, data._8 + data_count);
//        break;
//    case 16:
//        fft::compute(data._16, data._16 + data_count);
//        break;
//    }

//}

//    std::vector<double> r;
//    r.resize(data_count); // 自相关
//    switch (bits)
//    {
//    case 8:
//        for (std::size_t i = 0; i < r.size(); ++i)
//        {
//            for (std::size_t j = i; j < r.size(); ++j)
//                r[i] += data._8[j] * data._8[j-i];
//            r[i] /= (0x7F * 0x7F);
//        }
//        break;
//    case 16:
//        for (std::size_t i = 0; i < r.size(); ++i)
//        {
//            for (std::size_t j = i; j < r.size(); ++j)
//                r[i] += data._16[j] * data._16[j-i];
//            r[i] /= (0x7FFF * 0x7FFF);
//        }
//        break;
//    }
//    return [this](double omega)
//    {
//        double p = 0;
//        for (int i = 0; i < r.size(); ++i)
//            p += r[i] * std::cos(omega * i);
//        return p;
//    };
